package com.lord.otter.component.tools.business.chain;

import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.lord.otter.component.infrastructure.facade.json.JSONFacade;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
@RequiredArgsConstructor
public class BusinessHandlerLogAppender implements ApplicationRunner {

    private final BusinessHandlerLogRepository businessHandlerLogRepository;

    ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1,
            new ThreadFactoryBuilder().setNamePrefix("BusinessHandlerLogAppenderThread").build());

    private ConcurrentLinkedQueue<BusinessHandlerLog> queue = new ConcurrentLinkedQueue<>();


    public void addSuccessLog(
            String execId,
            String name,
            int execSeq,
            BusinessContext context,
            Object command
    ) {
        String s = Objects.nonNull(context) ? JSONFacade.toJSONString(context) : "";
        String c = Objects.nonNull(command) ? JSONFacade.toJSONString(command) : "";
        BusinessHandlerLog log = BusinessHandlerLog.create(execId, execSeq, name, s, BusinessHandlerStatus.SUCCESS, c,
                null, null);
        addQueue(log);
    }


    public void addFailLog(String execId, String name, int execSeq, Object command,
                           BusinessContext context, Exception e) {
        BusinessHandlerLog failLog = createFailLog(execId, name, execSeq, command, context, e);
        addQueue(failLog);
    }

    private BusinessHandlerLog createFailLog(String execId, String name, int execSeq, Object command,
                                             BusinessContext context, Exception e) {
        StackTraceElement[] stackTrace = e.getStackTrace();
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("异常信息:").append(e.getMessage()).append("\n");
        for (int i = 0; i < stackTrace.length && i < 1000; i++) {
            StackTraceElement stackTraceElement = stackTrace[i];
            stringBuilder.append(stackTraceElement.toString()).append("\n");
        }
        String c = Objects.nonNull(command) ? JSONFacade.toJSONString(command) : "";
        String s = Objects.nonNull(context) ? JSONFacade.toJSONString(context) : "";
        return BusinessHandlerLog.create(execId, execSeq, name, s, BusinessHandlerStatus.FAIL, c,
                e.getMessage(), stringBuilder.toString());
    }


    private void addQueue(BusinessHandlerLog handlerLog) {
        if (queue.size() > 30000) {
            log.error("业务执行器日志超过3万条没有处理，超出容量，请排查");
            return;
        }
        queue.offer(handlerLog);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
            try {
                log.debug("执行业务执行器日志的保存");
                saveLog();
            } catch (Exception e) {
                log.error("保存业务执行器日志错误:", e);
            }
        }, 5, 5, TimeUnit.SECONDS);
    }

    private void saveLog() {
        List<BusinessHandlerLog> temp = new ArrayList<>(queue.size());
        BusinessHandlerLog poll;
        while ((poll = queue.poll()) != null) {
            temp.add(poll);
        }
        if (CollectionUtils.isEmpty(temp)) {
            return;
        }
        businessHandlerLogRepository.add(temp);
    }

}
